package mh.message;

import java.util.ArrayList;
import java.util.TreeMap;

/**
 * This class is not properly a parser. It just analyze if IrcMessage has the right parameters structure for the
 * correspondent command. The class is used in the heart of Command Recognizer and MessageReceiver to complete parse
 * operations, so the reason of its name.
 * 
 * @author Gianmarco Laggia
 * 
 */
public class MessageValidator {

	/*
	 * This matrix take store of each irc command structure. Each row is a trio: "COMMAND", #request params, #accepted
	 * params
	 */

	private static final int				REQ_PARAM_INDEX	= 0;
	private static final int				ACC_PARAM_INDEX	= 1;

	private static TreeMap<String, int[]>	commands;

	public MessageValidator() {

		commands = new TreeMap<String, int[]>();
		commands.put("HELP", new int[] { 0, 1 });
		commands.put("QUOTE", new int[] { 1, 0 });
		commands.put("JOIN", new int[] { 0, 2 });
		commands.put("PART", new int[] { 0, 2 });
		commands.put("PING", new int[] { 0, 2 });
		commands.put("PONG", new int[] { 1, 2 });
		commands.put("QUIT", new int[] { 0, 1 });
		commands.put("NICK", new int[] { 0, 1 });
		commands.put("ME", new int[] { 1, 1 });
		commands.put("CTCP", new int[] { 2, 2 });
		commands.put("KBAN", new int[] { 1, 2 });
		commands.put("IGNORE", new int[] { 0, 1 });
		commands.put("UNIGNORE", new int[] { 1, 1 });
		commands.put("SERVER", new int[] { 1, 2 });
		commands.put("QUERY", new int[] { 1, 2 });

		//Standard IRC commands.    	//Standard IRC commands.
		commands.put("MOVE", new int[] { 1, 2 });
		commands.put("MODE", new int[] { 0, 4 });
		commands.put("SQUIT", new int[] { 2, 2 });
		commands.put("TOPIC", new int[] { 0, 2 });
		commands.put("NAMES", new int[] { 0, 2 });
		commands.put("LIST", new int[] { 0, 2 });
		commands.put("INVITE", new int[] { 0, 2 });
		commands.put("KICK", new int[] { 0, 3 });
		commands.put("PRIVMSG", new int[] { 1, 2 });
		commands.put("NOTICE", new int[] { 2, 2 });
		commands.put("MOTD", new int[] { 0, 1 });
		commands.put("LUSERS", new int[] { 0, 2 });
		commands.put("VERSION", new int[] { 0, 1 });
		commands.put("STATS", new int[] { 0, 2 });
		commands.put("LINKS", new int[] { 1, 2 });
		commands.put("TIME", new int[] { 0, 1 });
		commands.put("CONNECT", new int[] { 2, 3 });
		commands.put("TRACE", new int[] { 0, 1 });
		commands.put("ADMIN", new int[] { 0, 1 });
		commands.put("INFO", new int[] { 0, 1 });
		commands.put("WHO", new int[] { 0, 2 });
		commands.put("WHOIS", new int[] { 0, 2 });
		commands.put("WHOWAS", new int[] { 0, 2 });
		commands.put("KILL", new int[] { 0, 2 });
		commands.put("AWAY", new int[] { 0, 1 });
		commands.put("PASS", new int[] { 1, 1 });
		commands.put("NICK", new int[] { 1, 1 });
		commands.put("USER", new int[] { 1, 1 });
		commands.put("USERHOST", new int[] { 0, 5 });
		commands.put("NS", new int[] { 0, 1 });
		//PARIPARI
		commands.put("PARIPARI_RECON", new int[] { 2, 2 });

	}

	/**
	 * Returns #required params for String cmd corrispondent command. If cmd is undefinded command -1 is returned.
	 * 
	 * @param cmd
	 */
	public static int getRequiredParams(String cmd) {

		if ((cmd == null) || !commands.containsKey(cmd)) {
			return -1;
		} else {
			return commands.get(cmd)[REQ_PARAM_INDEX];
		}

	}

	/**
	 * Returns #accepted params for String cmd corrispondent command. If cmd is undefinded command -1 is returned.
	 * 
	 * @param cmd
	 */
	public static int getAcceptedParams(String cmd) {

		if ((cmd == null) || !commands.containsKey(cmd)) {
			return -1;
		} else {
			return commands.get(cmd)[ACC_PARAM_INDEX];
		}

	}

	/**
	 * Returns true if IrcMessage msg is acceptable, false otherwise. msg is acceptable if his params number corrisponds
	 * to #acceptable or #required for its command. If the command is not recognized it's assumed as a valid unknown
	 * server message.
	 * 
	 * @param cmd
	 */
	public static boolean isAcceptable(IncomingMessage msg) {

		String cmd = msg.getCommand();

		/*
		 * if we have a numeric response command from server we can assume it as always valid.
		 */
		try {
			int tmp = Integer.parseInt(cmd);
			return true;
		} catch (NumberFormatException e) {
			//ignore...it's not numeric, so control will be made.
		}

		ArrayList<String> pars = msg.getParams();

		//command not in table, not recognized!
		if ((cmd != "") && (cmd != null)) {
			if (!commands.containsKey(cmd)) {
				return false;
			}
		}

		//parameters not accepted for this command
		if ((pars.size() > getAcceptedParams(cmd)) || (pars.size() < getRequiredParams(cmd))) {

			return false;
		} else {

			return true;
		}
	}
}
